﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Linq;

namespace Inaction
{
    [Serializable]
    public abstract class RemotingMethod : Core.IDbQuerable
    {
        #region 静态方法

        #region 执行远程方法RemotingMethod
        public static T Invoke<T>(T method) where T : RemotingMethod
        {
            return (T)Inaction.Proxy.Invoke(method);
        }
        #endregion

        #region 远程静态方法Method
        public static T Invoke<T>(Delegate func, params object[] paras)
        {
            var method = new StaticRemotingMethod(func.Method, paras);
            var retMethod = (StaticRemotingMethod)Inaction.Proxy.Invoke(method);
            if (retMethod.Result == null || retMethod.Result == DBNull.Value) return default(T);
            return (T)retMethod.Result;
        }

        public static T Invoke<T>(Type hostType, string methodName, params object[] paras)
        {
            var method = new NamedRemotingMethod(hostType, methodName, paras);
            var retMethod = (NamedRemotingMethod)Inaction.Proxy.Invoke(method);
            if (retMethod.Result == null || retMethod.Result == DBNull.Value) return default(T);
            return (T)retMethod.Result;
        }
        public static T Invoke<T>(MethodInfo methodInfo, params object[] paras)
        {
            var method = new StaticRemotingMethod(methodInfo, paras);
            var retMethod = (StaticRemotingMethod)Inaction.Proxy.Invoke(method);
            if (retMethod.Result == null || retMethod.Result == DBNull.Value) return default(T);
            return (T)retMethod.Result;
        }
        public static void Invoke(Action func, params object[] paras)
        {
            var method = new StaticRemotingMethod(func.Method, paras);
            Inaction.Proxy.Invoke(method);
        }
        public static void Invoke(MethodInfo methodInfo, params object[] paras)
        {
            var method = new StaticRemotingMethod(methodInfo, paras);
            Inaction.Proxy.Invoke(method);
        }
        #endregion

        #region 远程对象方法Method
        public static T Invoke<T>(object instance, Delegate func, params object[] paras)
        {
            var method = new InstanceRemotingMethod(instance, func.Method, paras);
            var retMethod = (InstanceRemotingMethod)Inaction.Proxy.Invoke(method);
            if (retMethod.Result == null || retMethod.Result == DBNull.Value) return default(T);
            return (T)retMethod.Result;
        }
        public static T Invoke<T>(object instance, MethodInfo methodInfo, params object[] paras)
        {
            var method = new InstanceRemotingMethod(instance, methodInfo, paras);
            var retMethod = (InstanceRemotingMethod)Inaction.Proxy.Invoke(method);
            if (retMethod.Result == null || retMethod.Result == DBNull.Value) return default(T);
            return (T)retMethod.Result;
        }

        public static T Invoke<T>(T instance, Delegate func, params object[] paras)
        {
            var method = new InstanceRemotingMethod(instance, func.Method, paras);
            var retMethod = (InstanceRemotingMethod)Inaction.Proxy.Invoke(method);
            if (retMethod.Result == null || retMethod.Result == DBNull.Value) return default(T);
            return (T)retMethod.Instance;
        }
        public static T Invoke<T>(T instance, MethodInfo methodInfo, params object[] paras)
        {
            var method = new InstanceRemotingMethod(instance, methodInfo, paras);
            var retMethod = (InstanceRemotingMethod)Inaction.Proxy.Invoke(method);
            if (retMethod.Result == null || retMethod.Result == DBNull.Value) return default(T);
            return (T)retMethod.Instance;
        }
        public static void Invoke(object instance, Action func, params object[] paras)
        {
            var method = new InstanceRemotingMethod(instance, func.Method, paras);
            Inaction.Proxy.Invoke(method);
        }
        public static void Invoke(object instance, MethodInfo methodInfo, params object[] paras)
        {
            var method = new InstanceRemotingMethod(instance, methodInfo, paras);
            Inaction.Proxy.Invoke(method);
        }
        #endregion

        #endregion

        #region
        protected abstract void Invoke();

        internal void DoInvoke()
        {
            Invoke();
        }

        #region IDbQuerable
        public string ParaFix
        {
            get { return Inaction.DbProvider.Instance.ParaFix; }
        }
        #endregion

        #endregion
    }

    class InstanceRemotingMethod : StaticRemotingMethod
    {
        object instance;

        public object Instance
        {
            get { return instance; }
        }
        public InstanceRemotingMethod(object instance, MethodInfo method, params object[] paras)
            : base(method, paras)
        {
            this.instance = instance;
        }
        protected override void Invoke()
        {
            if (Method.ReturnType != null)
                Result = Method.Invoke(instance, Paras);
            else
                Method.Invoke(instance, Paras);
        }
    }

    [Serializable]
    class StaticRemotingMethod : RemotingMethod
    {
        MethodInfo method;
        public MethodInfo Method
        {
            get { return method; }
            set { method = value; }
        }

        object[] paras;
        public object[] Paras
        {
            get { return paras; }
            set { paras = value; }
        }

        object result = null;
        public object Result
        {
            get { return result; }
            set { result = value; }
        }
        public StaticRemotingMethod(MethodInfo method, params object[] paras)
        {
            this.method = method;
            this.paras = paras;
        }
        protected override void Invoke()
        {
            if(paras==null || paras.Length==0)
            {
                result = method.Invoke(null,null);
            }
            else
            {
                result = method.Invoke(null, paras);
            }
        }
    }

    [Serializable]
    public class NamedRemotingMethod : RemotingMethod
    {
        MethodInfo method = null;
        MethodInfo Method
        {
            get
            {
                if (method == null)
                {
                    method = Inaction.Util.TypeHelper.GetStaticMethodInfo(HostType, MethodName, Paras.Select(p => p.GetType()).ToArray());
                }
                return method;
            }
        }
        public string MethodName { get; set; }
        public Type HostType { get; set; }
        object[] paras;
        public object[] Paras
        {
            get { return paras; }
            set { paras = value; }
        }

        object result = null;
        public object Result
        {
            get { return result; }
            set { result = value; }
        }
        public NamedRemotingMethod(Type hostType, string methodName, params object[] paras)
        {
            HostType = hostType;
            MethodName = methodName;
            this.paras = paras;
        }
        protected override void Invoke()
        {
            if (Method.ReturnType != null)
                result = Method.Invoke(null, paras);
            else
                Method.Invoke(null, paras);
        }
    }
}